# screen_recorder.py
import subprocess
import time
import logging
import os
import threading
import sys
from pathlib import Path
import signal
import shutil
import platform
import json

logger = logging.getLogger()


class ScreenRecorder:
    def __init__(self, device_serial, recording_dir):
        """屏幕录制器初始化"""
        self.device_serial = device_serial
        self.recording_dir = recording_dir
        self.process = None
        self.output_file = None
        self.is_recording = False
        self.stop_requested = False
        self.scrcpy_path = self.find_scrcpy()
        self.use_adb = False
        self.monitor_thread = None
        self.output_queue = []

        # 录屏诊断信息
        self.recording_errors = []
        self.recording_warnings = []

        if self.scrcpy_path:
            logger.info(f"✅ 找到Scrcpy: {self.scrcpy_path}")
        else:
            logger.warning("⚠️ 未找到Scrcpy可执行文件，将使用ADB录制")

    def find_scrcpy(self):
        """查找Scrcpy可执行文件路径"""
        # 1. 检查系统PATH中是否有scrcpy
        scrcpy_path = shutil.which("scrcpy")
        if scrcpy_path:
            return scrcpy_path

        # 2. Windows特定路径
        if platform.system() == "Windows":
            possible_paths = [
                Path.home() / "scoop" / "shims" / "scrcpy.exe",
                Path("C:/Program Files/scrcpy/scrcpy.exe"),
                Path("C:/Program Files (x86)/scrcpy/scrcpy.exe"),
                Path("D:/Program Files/scrcpy/scrcpy.exe"),
                Path("D:/Program Files (x86)/scrcpy/scrcpy.exe"),
            ]

            for path in possible_paths:
                if path.exists():
                    return str(path)

        # 3. macOS/Linux路径
        elif platform.system() in ["Darwin", "Linux"]:
            possible_paths = [
                "/usr/bin/scrcpy",
                "/usr/local/bin/scrcpy",
                Path.home() / ".local/bin/scrcpy",
                "/opt/homebrew/bin/scrcpy"  # macOS M1路径
            ]

            for path in possible_paths:
                if os.path.exists(path):
                    return path

        # 4. 尝试通过adb路径推断
        adb_path = shutil.which("adb")
        if adb_path:
            # 在adb同级目录查找
            adb_dir = Path(adb_path).parent
            possible_paths = [
                adb_dir / "scrcpy",
                adb_dir / "scrcpy.exe"
            ]

            for path in possible_paths:
                if path.exists():
                    return str(path)

        return None

    def start(self, case_name, is_retry=False):
        """启动屏幕录制"""
        retry_suffix = "_retry" if is_retry else ""
        timestamp = time.strftime("%Y%m%d%H%M%S")

        # 确保录屏目录存在
        self.recording_dir.mkdir(parents=True, exist_ok=True)

        # 创建输出文件
        filename = f"{case_name}{retry_suffix}_{timestamp}.mp4"
        self.output_file = self.recording_dir / filename

        # 优先尝试Scrcpy录制
        if self.scrcpy_path:
            return self.start_scrcpy(case_name)
        else:
            return self.start_adb_recording(case_name)

    def start_scrcpy(self, case_name):
        """使用Scrcpy启动录制"""
        try:
            logger.info(f"🟢 启动Scrcpy录制: {case_name}")

            # 使用更简单的参数设置
            command = [
                self.scrcpy_path,
                "-s", self.device_serial,
                "--no-playback",
                "--record", str(self.output_file),
                "--no-audio",
                "--max-fps", "15",  # 降低帧率提高兼容性
                "--verbosity", "debug"
            ]

            logger.debug(f"Scrcpy命令: {' '.join(command)}")

            # Windows特定设置
            startupinfo = None
            if platform.system() == "Windows":
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                startupinfo.wShowWindow = 0  # 隐藏窗口

            # 启动进程
            self.process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                encoding='utf-8',
                errors='replace',
                startupinfo=startupinfo
            )
            self.is_recording = True
            self.use_adb = False

            # 启动输出收集线程
            self.output_queue = []
            self.monitor_thread = threading.Thread(target=self._collect_output)
            self.monitor_thread.daemon = True
            self.monitor_thread.start()

            # 等待2秒检查进程状态
            time.sleep(2)

            if self.process.poll() is not None:
                output = self.get_output()
                error_msg = f"Scrcpy启动失败: {output[:200] if output else '无输出'}"
                logger.error(error_msg)
                return False

            return True
        except Exception as e:
            logger.error(f"Scrcpy启动异常: {str(e)}")
            return False

    def start_adb_recording(self, case_name):
        """使用ADB启动录制"""
        try:
            logger.info(f"🟢 启动ADB录制: {case_name}")

            # 设备上的临时文件路径
            device_path = f"/sdcard/{self.output_file.name}"

            # ADB录制命令
            command = [
                "adb", "-s", self.device_serial,
                "shell", "screenrecord", "--bit-rate", "4M", device_path
            ]

            logger.debug(f"ADB命令: {' '.join(command)}")

            # 启动进程
            self.process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                encoding='utf-8',
                errors='replace'
            )
            self.is_recording = True
            self.use_adb = True

            # 启动输出收集线程
            self.output_queue = []
            self.monitor_thread = threading.Thread(target=self._collect_output)
            self.monitor_thread.daemon = True
            self.monitor_thread.start()

            # 等待2秒检查进程状态
            time.sleep(2)

            if self.process.poll() is not None:
                output = self.get_output()
                error_msg = f"ADB录制启动失败: {output[:200] if output else '无输出'}"
                logger.error(error_msg)
                return False

            return True
        except Exception as e:
            logger.error(f"ADB启动异常: {str(e)}")
            return False

    def _collect_output(self):
        """收集进程输出"""
        try:
            while self.is_recording and not self.stop_requested:
                if self.process and self.process.stdout:
                    line = self.process.stdout.readline()
                    if line:
                        self.output_queue.append(line)
                        # 检测关键错误信息
                        if "error" in line.lower() or "fail" in line.lower():
                            logger.error(f"录制错误: {line.strip()}")
                else:
                    time.sleep(0.1)
        except Exception as e:
            logger.error(f"输出收集异常: {str(e)}")

    def get_output(self):
        """获取收集到的输出"""
        return "".join(self.output_queue)

    def is_valid_video(self, file_path):
        """验证视频文件是否完整 (使用FFprobe)"""
        try:
            # 检查文件是否存在
            if not file_path.exists():
                logger.error(f"视频文件不存在: {file_path}")
                return False

            file_size = file_path.stat().st_size
            if file_size < 1024:  # 小于1KB视为无效
                logger.error(f"视频文件大小异常: {file_size}字节")
                return False

            # 如果找不到ffprobe，只检查大小
            ffprobe_path = shutil.which("ffprobe")
            if not ffprobe_path:
                logger.warning("⚠️ 未找到ffprobe，仅检查文件大小")
                return True

            command = [
                ffprobe_path,
                "-v", "error",
                "-show_format",
                "-show_streams",
                "-print_format", "json",
                str(file_path)
            ]

            result = subprocess.run(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                timeout=10
            )

            if result.returncode != 0:
                logger.error(f"视频验证失败: {result.stderr.decode('utf-8')[:500]}")
                return False

            # 检查视频流信息
            probe_data = json.loads(result.stdout)
            if not probe_data.get("streams"):
                logger.error("视频文件中未找到有效流")
                return False

            return True
        except Exception as e:
            logger.error(f"视频验证异常: {str(e)}")
            return False

    def stop(self):
        """停止屏幕录制"""
        if not self.is_recording:
            logger.info("未启动录制，无需停止")
            return None

        self.stop_requested = True
        logger.info("🛑 正在停止录制...")

        try:
            # 使用上下文管理器临时忽略信号
            original_handler = signal.getsignal(signal.SIGINT)
            signal.signal(signal.SIGINT, signal.SIG_IGN)

            try:
                # 优先使用更安全的停止方式
                if self.use_adb:
                    # ADB录屏使用SIGINT是安全的
                    if platform.system() == "Windows":
                        self.process.send_signal(signal.CTRL_C_EVENT)
                    else:
                        self.process.terminate()
                else:
                    # Scrcpy使用优雅的停止方式
                    if platform.system() == "Windows":
                        # 尝试优雅停止
                        try:
                            self.process.send_signal(signal.CTRL_C_EVENT)
                            logger.info("发送Ctrl+C信号给Scrcpy进程")
                            # 等待进程优雅退出
                            self.process.wait(timeout=10)
                        except (subprocess.TimeoutExpired, AttributeError):
                            # 优雅停止失败时使用强制终止
                            logger.warning("优雅停止失败，强制终止录屏进程")
                            subprocess.run(
                                ["taskkill", "/F", "/T", "/PID", str(self.process.pid)],
                                stdout=subprocess.DEVNULL,
                                stderr=subprocess.DEVNULL
                            )
                    else:
                        # Unix系统使用SIGTERM
                        self.process.terminate()

                # 等待进程结束
                try:
                    self.process.wait(timeout=15)  # 增加等待时间
                except subprocess.TimeoutExpired:
                    logger.warning("🕒 录屏进程未正常退出，强制终止")
                    self.process.kill()
                    self.process.wait(timeout=5)
            finally:
                # 恢复原来的信号处理器
                signal.signal(signal.SIGINT, original_handler)

            # 增加等待时间确保文件写入
            logger.info("等待文件写入完成...")
            time.sleep(5)  # 重要：增加等待时间

            # 对于ADB录制，需要从设备拉取文件
            if self.use_adb:
                file = self.pull_adb_recording()
            else:
                file = self.output_file

            # 验证录屏文件
            if file and file.exists():
                # 强制刷新文件系统缓冲区
                try:
                    logger.info("刷新文件系统缓冲区...")
                    with open(file, 'a') as f:
                        os.fsync(f.fileno())
                except Exception as e:
                    logger.warning(f"刷新缓冲区失败: {str(e)}")

                # 额外等待确保文件同步
                time.sleep(2)

                # 验证文件有效性
                if self.is_valid_video(file):
                    logger.info(f"✅ 录屏文件验证通过: {file}")
                    return file
                else:
                    logger.error("❌ 录屏文件验证失败，可能已损坏")
                    return None
            else:
                logger.error("❌ 录屏文件未生成")
                return None
        except Exception as e:
            logger.error(f"停止录制失败: {str(e)}")
            return None
        finally:
            self.is_recording = False
            self.stop_requested = False

            # 确保线程结束
            if self.monitor_thread and self.monitor_thread.is_alive():
                self.monitor_thread.join(timeout=1.0)

    def pull_adb_recording(self):
        """拉取ADB录制的文件"""
        try:
            device_path = f"/sdcard/{self.output_file.name}"
            logger.info(f"从设备拉取录屏文件: {device_path} -> {self.output_file}")

            result = subprocess.run(
                ["adb", "-s", self.device_serial, "pull", device_path, str(self.output_file)],
                capture_output=True,
                text=True,
                timeout=60  # 增加超时时间到60秒
            )

            if result.returncode == 0:
                # 删除设备上的文件
                subprocess.run(
                    ["adb", "-s", self.device_serial, "shell", "rm", device_path],
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL
                )

                if self.output_file.exists():
                    return self.output_file
                else:
                    logger.error("❌ ADB录屏文件拉取后不存在")
            else:
                logger.error(f"❌ 拉取录屏失败: {result.stderr}")

            return None
        except Exception as e:
            logger.error(f"❌ 拉取ADB录屏失败: {str(e)}")
            return None